<?php
/**
 * Copyright (c) 2006-2008, Julien PORTALIER
 * http://featherphp.googlecode.com/
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 */

define('ORIG_INCLUDE_PATH', ini_get('include_path'));
ini_set('include_path',
	ORIG_INCLUDE_PATH.PATH_SEPARATOR.
	APP.PATH_SEPARATOR.
	FEATHER.PATH_SEPARATOR.
	dirname(FEATHER)
);

/**
 * Automatic class inclusion.
 */
function __autoload($class)
{
	if (preg_match('/(Controller|Model|Component|Behavior)$/', $class, &$match))
	{
		$file = Inflector::underscore(str_replace($match[1], '', $class));
		$path = Inflector::tableize($match[1]);
		
		if ($path == 'components') {
			$path = 'controllers'.DS.'components';
		}
		elseif ($path == 'behaviors') {
			$path = 'models'.DS.'behaviors';
		}
	}
	elseif (preg_match('/^ActiveRecord/', $class))
	{
		$file = Inflector::underscore($class);
		$path = 'models';
	}
	else {
		return false;
	}
	require_once $path.DS."$file.php";
}

/**
 * Handles load exceptions.
 */
class Load_Exception extends Exception { }

/**
 * Basic classes and files loader.
 */
class Load
{
	const   FileNotFound = 1;
	
	private $controller;
	private $view;
#	private static $instance;
	
	/*private*/ function __construct(Controller $Controller)
	{
		$this->controller = $Controller;
	}
	
	/**
	 * Returns the current load instance (create it if it doesn't exists).
	 * 
	 * @return Load instance.
	 * @param $controller Controller[optional]
	 */
	/*
	function instance(Controller $controller=null)
	{
		if (!isset(self::$instance)) {
			self::$instance = new self($controller);
		}
		return self::$instance;
	}
	*/
	
	/**
	 * Sets the currently used plugin.
	 * 
	 * @param $_plugin String
	 */
	static function set_plugin($_plugin)
	{
		Core::$plugin = $_plugin;
		ini_set('include_path',
			ORIG_INCLUDE_PATH.PATH_SEPARATOR.
			APP.'plugins'.DS.$_plugin.PATH_SEPARATOR.
			APP.PATH_SEPARATOR.
			FEATHER.PATH_SEPARATOR.
			dirname(FEATHER)
		);
		if (!defined('PLUGIN_DB_PREFIX'))
		{
			define('PLUGIN_DB_PREFIX', $_plugin.'_');
		}
	}
	
	
	// generics
	
	/**
	 * Includes a file.
	 * 
	 * @param $path String
	 * @param $files Mixed Array or String.
	 * @deprecated use require_once "$path/$file" instead;
	 */
	static function _include($path, $files)
	{
		if (!is_array($files))
		{
			$files = func_get_args();
			$path  = array_shift($files);
		}
		
#		debug("Load::_include($path, ".implode(', ', $files).");");
		foreach($files as $file)
		{
			if (!DEBUG)
			{
				if (!@include_once "$path/$file.php") {
					throw new Load_Exception("Can't find '$path/$file.php'.", Load::FileNotFound);
				}
			}
			else
			{
				if (!include_once "$path/$file.php") {
					throw new Load_Exception("Can't find '$path/$file.php'.", Load::FileNotFound);
				}
			}
		}
	}
	
	/**
	 * Includes one or many internal libs.
	 */
	static function libs($file)
	{
		$args = func_get_args();
		foreach($args as $lib) {
			require_once 'libs'.DS.$lib.'.php';
		}
	}
	
	/**
	 * Includes vendors' libs.
	 * 
	 * @deprecated Use require_once "vendors/$file.php" instead.
	 */
	static function vendors($file)
	{
		trigger_error('Load::vendors() is deprecated, use require_once("vendors/$file") instead.', E_USER_NOTICE);
		require_once 'vendors'.DS.$file;
	}
	
	
	// CONTROLLERS
	
	/**
	 * Loads up a controller.
	 * 
	 * @return Controller
	 * @param $name   String
	 * @param $uri    String
	 * @param $action String[optional]
	 * @param $args   Array[optional]
	 */
	static function controller($name, $uri, $action='index', array &$args=null)
	{
		try
		{
#			self::_include('views', 'view');
#			self::_include('controllers', 'controller', 'application_controller', $name);
			require_once 'views'.DS.'view.php'; 
			require_once 'controllers'.DS.'controller.php';
			require_once 'controllers'.DS.'application_controller.php';
			require_once 'controllers'.DS."$name.php";
		}
		catch(Exception $e) {
			Error::show(404);
		}
		
		# creates controller
		$class      = Inflector::camelize($name).'Controller';
		$controller = new $class($uri, $action, $args);
		
		# loads components (if any)
		if (!empty(Core::$components) or !empty($controller->components))
		{
			$components = array_merge(Core::$components, $controller->components);
			$controller->load->components($components);
		}
		return $controller;
	}
	
	/**
	 * Loads up a script.
	 * 
	 * @return Script
	 * @param $name String
	 * @param $action String[optional]
	 * @param $params Array[optional]
	 */
	static function script($name, $action='main', array $params=null)
	{
		try
		{
#			self::_include('views', 'view');
#			self::_include('controllers', 'controller');
#			self::_include('scripts', 'script', 'application_script', $name);
			require_once 'views'.DS.'view.php'; 
			require_once 'controllers'.DS.'controller.php';
			require_once 'scripts'.DS.'script.php';
			require_once 'scripts'.DS.'application_script.php';
			require_once 'scripts'.DS."$name.php";
		}
		catch(Exception $e)
		{
			Error::show(404);
		}
		
		# creates script
		$class  = Inflector::camelize($name).'Script';
		$script = new $class($action, $params);
		
		# loads components (if any)
		if (!empty(Core::$components) or !empty($script->components))
		{
			$components = array_merge(Core::$components, $script->components);
			$script->load->components($components);
		}
		
		return $script;
	}
	
	/**
	 * Loads components.
	 *  
	 * @param $components Array
	 */
	function components(array $components)
	{
		foreach($components as $component) {
			$this->component($component);
		}
	}
	
	/**
	 * Loads a component.
	 * 
	 * @param $class String
	 */
	function component($class)
	{
		if (!isset($this->controller->$class))
		{
#			self::_include('controllers'.DS.'components', Inflector::Underscore($class));
			require_once 'controllers'.DS.'components'.DS.Inflector::Underscore($class).'.php';
			
			# creates components
			$ComponentClass = "{$class}Component";
			$Component = new $ComponentClass();
			$this->controller->$class = $Component;
			
			# starts it up
			if (method_exists($Component, 'startup')
				and $this->controller instanceof Controller)
			{
				$Component->startup($this->controller);
			}
			
			# bind callbacks
			if (isset($Component->bind_to))
			{
				foreach($Component->bind_to as $action => $funcs)
				{
					if (!is_array($funcs)) {
						$funcs = explode(',', $funcs);
					}
					foreach($funcs as $func) {
						$this->controller->bind_callback($action, $class, trim($func));
					}
				}
			}
		}
		else {
			trigger_error(get_class($this->controller)."::$class has already been declared.", E_USER_WARNING);
		}
	}
	
	
	// MODELS
	
	/**
	 * Includes a model definition.
	 * 
	 * @param $model String
	 */
	static function model($model)
	{
		try
		{
			#self::_include('models', Inflector::underscore($model));
			require_once 'models'.DS.Inflector::underscore($model).'.php';
		}
		catch(Load_Exception $e)
		{
			# silences load error
			return false;
		}
		return true;
	}
	
	/**
	 * Instanciates a new model.
	 * 
	 * @param $model String
	 */
	static function new_model($model)
	{
		Load::model($model);
		return new $model;
	}
	
	/**
	 * Loads and attaches a behavior to a model.
	 * 
	 * @param $class String
	 * @param $model Object
	 */
	static function behavior($class, Object $model)
	{
		$_class = "_{$class}";
		if (!isset($model->$_class))
		{
			require_once 'models'.DS.'behaviors'.DS.Inflector::Underscore($class).'.php';
			$BehaviorClass = "{$class}Behavior";
			$model->$_class = new $BehaviorClass($model);
		}
		else {
			trigger_error(get_class($model)."::$class has already been declared.", E_USER_WARNING);
		}
	}
	
	
	// VIEWS
	
	/**
	 * Loads helpers.
	 * 
	 * @param $helpers Array
	 */
	static function helpers(array $helpers)
	{
#		self::_include('views'.DS.'helpers', &$helpers);
		foreach($helpers as $helper)
		{
			require_once 'views'.DS.'helpers'.DS."$helper.php";
		}
	}
	
	/**
	 * Loads a helper
	 * 
	 * @param $helper String
	 */
	static function helper($helper)
	{
		require_once 'views'.DS.'helpers'.DS."$helper.php";
#		self::_include('views'.DS.'helpers', $helper);
	}
}
?>